
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

#include <share.h>
#include <windows.h>

#include "zmisc_cache.h"

struct zmisc_cache
{
    void           **pp_cache_data;
    size_t         s_max_cache_count;
    size_t         s_cached_count;
    size_t         s_entry_size;
    volatile long  vl_lock;
};


void *zmisc_init_cache(size_t s_max_cache_count, size_t s_entry_size )
{
    struct zmisc_cache *pst_cache             = (struct zmisc_cache *)0;
    int                i_result               = -1;
    size_t             s_size_malloc          = s_max_cache_count * sizeof(void *);

    for ( ; ; )
    {
        if ( s_max_cache_count >= (((size_t)-1) / sizeof(void *)) )
        {
            break;
        }

        pst_cache = (struct zmisc_cache *)malloc( sizeof(struct zmisc_cache) );
        if ( (struct zmisc_cache *)0 == pst_cache )
        {
            break;
        }

        pst_cache->s_cached_count    = 0;
        pst_cache->s_max_cache_count = s_max_cache_count;
        pst_cache->vl_lock           = 0;
        pst_cache->s_entry_size      = s_entry_size;

        pst_cache->pp_cache_data = (void **)malloc( s_size_malloc );
        if ((void**)0 == pst_cache->pp_cache_data)
        {
            break;
        }

        memset( pst_cache->pp_cache_data, 0, s_size_malloc );

        InterlockedExchange( &pst_cache->vl_lock, 0);

        i_result = 0;
        break;
    }

    if ( 0 != i_result )
    {
        zmisc_free_cache( pst_cache );
        pst_cache = (struct zmisc_cache *)0;
    }

    return pst_cache;
}


int zmisc_free_cache( void *p_cache )
{
    struct zmisc_cache *pst_cache    = (struct zmisc_cache *)p_cache;
    size_t             s_cache_index = 0;

    if ( (struct zmisc_cache *)0 == pst_cache )
    {
        return -1;
    }

    while (InterlockedCompareExchange(&pst_cache->vl_lock, -1, 0))
    {
        Sleep(0);
    }

    if ( (void **)0 != pst_cache->pp_cache_data )
    {
        for ( s_cache_index = 0; s_cache_index < pst_cache->s_cached_count; ++s_cache_index )
        {
            free( pst_cache->pp_cache_data[s_cache_index] );
            pst_cache->pp_cache_data[s_cache_index] = (void *)0;
        }
        free( pst_cache->pp_cache_data );
        pst_cache->pp_cache_data = (void **)0;
    }

    free( pst_cache );

    return 0;
}


void *zmisc_alloc_cache_data( void *p_cache )
{
    void                *pst_cache_data = (void *)0;
    struct zmisc_cache  *pst_cache      = (struct zmisc_cache *)p_cache;

    if ( (struct zmisc_cache *)0 == pst_cache )
    {
        return (void *)0;
    }

    while (InterlockedCompareExchange(&pst_cache->vl_lock, -1, 0))
    {
        Sleep(0);
    }

    if ( 0 < pst_cache->s_cached_count )
    {
        --pst_cache->s_cached_count;
        pst_cache_data = pst_cache->pp_cache_data[pst_cache->s_cached_count];
        pst_cache->pp_cache_data[pst_cache->s_cached_count] = (void *)0;
    }

    InterlockedExchange( &pst_cache->vl_lock, 0);

    if ( (void *)0 == pst_cache_data )
    {
        pst_cache_data = malloc( pst_cache->s_entry_size );
    }

    return pst_cache_data;
}


int zmisc_free_cache_data( void *p_cache, void *p_cache_data )
{
    struct zmisc_cache *pst_cache = (struct zmisc_cache *)p_cache;

    while (InterlockedCompareExchange(&pst_cache->vl_lock, -1, 0))
    {
        Sleep(0);
    }

    if ( pst_cache->s_cached_count < pst_cache->s_max_cache_count )
    {
        pst_cache->pp_cache_data[pst_cache->s_cached_count] = p_cache_data;
        ++pst_cache->s_cached_count;
        p_cache_data = (void *)0;
    }

    InterlockedExchange( &pst_cache->vl_lock, 0);

    if ( (void *)0 != p_cache_data )
    {
        free( p_cache_data );
    }

    return 0;
}
